/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package compilador.analisadorSemantico;

import compilador.analisadorSintatico.simboloGramatical.NaoTerminal;
import compilador.analisadorSintatico.simboloGramatical.SimboloGramatical;
import compilador.analisadorSintatico.simboloGramatical.Terminal;
import compilador.tabelaDeSimbolos.Identificador;
import compilador.tabelaDeSimbolos.TabelaSimbolos;
import java.util.List;

/**
 *
 * @author Henrique
 */
public class ChecadorTipos {
    public static final boolean DEBUG = AnalisadorSemantico.DEBUG;
    
    private AnalisadorSemantico analisadorSemantico;

    public ChecadorTipos(AnalisadorSemantico analisadorSemantico) {
        this.analisadorSemantico = analisadorSemantico;
    }
    
    public void checar(){
        SimboloGramatical raiz = analisadorSemantico.getArvoreSintatica().getRaiz();
        buscaEmProfundidade(raiz,(NaoTerminal) raiz);
    }
    
    /**
     * Procura DEC_VARIAVEL, DEC_CONSTANTE, DEC_METODO, DEC_CLASSE_INTERNA, PARAM
     * @param sg
     */
    private void buscaEmProfundidade(SimboloGramatical sg, NaoTerminal escopo){
        List<SimboloGramatical> filhos;
        
        
        if(sg instanceof NaoTerminal){
            NaoTerminal nt = (NaoTerminal) sg;

            escopo = AnalisadorSemantico.procuraEscopo(nt, escopo);

            //procura novos filhos
            filhos = nt.getFilhos();
            
            for(SimboloGramatical sgFilho: filhos){
                buscaEmProfundidade(sgFilho, escopo);
            }
        
            calculaRetorno(nt, escopo);
        }

        return;
    }
    
    /**
     * Calcula e valida o retorno de um nó da arvore tendo como base os nós filhos JÁ CALCULADOS!!!!!
     * 
     * São necessários VALIDAR:
     * .if(<algo>) ...
     * .while(<algo>)
     * .atribuição
     * .passagem de parametros para função
     * .variavel[<algo>]
     * .folhas das expressões
     * .todos lugares que usam variavel
     * 
     * @param nt
     * @return
     */
    private String calculaRetorno(NaoTerminal nt, NaoTerminal escopo){
        String tipoResultante = null;
        //O fudimento está nessa função aqui
        String nome = nt.getNome();
        Identificador id;

        if(nome.equals("CHAMADA_METODO")){
            NaoTerminal nomeNaoTerminal = (NaoTerminal) nt.get(0);
            String nomeMetodo = AnalisadorSemantico.extraiIdDeNomeOuTipoPrimitivo(nomeNaoTerminal);
            
            id = TabelaSimbolos.getInstance().get(nomeMetodo, escopo);

            tipoResultante = id.getTipo();
            tipoResultante.replaceAll("METODO!", "");

            //TODO VALIDAR PARAMETROS NA ORDEM CORRETA
            /*if(!valido){
                insereErro(descricao, linha);
            }*/
            


        } else if(nome.equals("NRO")){
            tipoResultante = "int";
            int indexFilho = 0;

            //Checagem de tipo
            if(nt.get(indexFilho) instanceof Terminal){
                indexFilho ++;
            }
            if(nt.get(indexFilho) instanceof NaoTerminal){
                NaoTerminal ntFilho = (NaoTerminal) nt.get(indexFilho);
                if(ntFilho.getNome().equals("OP_INC"))
                    indexFilho ++;
            }
            if(nt.get(indexFilho) instanceof NaoTerminal){
                NaoTerminal ntFilho = (NaoTerminal) nt.get(indexFilho);

                if(ntFilho.getTipoResultante().equals("int")){
                    int linha = AnalisadorSemantico.getNumeroLinha(nt);
                    insereErroTipoInvalido("esperado valor numerico", linha);
                }
            }

        }else if(nome.equals("VALOR")){
            //charconst
            if(nt.get(0) instanceof Terminal){
                tipoResultante = "char[]";
            }
            //outros casos
            else if(nt.get(0) instanceof NaoTerminal){
                NaoTerminal ntFilho = (NaoTerminal) nt.get(0);

                tipoResultante = ntFilho.getTipoResultante();
            }
        }
        
        nt.setTipoResultante(tipoResultante);
        
        return tipoResultante;
    }

    private void insereErroTipoInvalido(String descricao, int linha){
        ErroSemantico erro = new ErroSemantico();
        erro.setDescricao(descricao);
        erro.setLinha(linha);
        
        erro.setTipo(TipoErroSemantico.TIPO_INVALIDO);

        analisadorSemantico.getErrosSemanticos().add(erro);
    }
    
}
